package cn.zzz.openplatform.txlbs;

import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import cn.zzz.openplatform.txlbs.constant.TxlbsUrlConstant;
import cn.zzz.openplatform.txlbs.model.TxlbsLocation;
import cn.zzz.openplatform.txlbs.model.TxlbsOpenApiRequest;
import cn.zzz.openplatform.txlbs.model.TxlbsOpenApiResponse;
import cn.zzz.openplatform.txlbs.model.search.TxlbsSuggestionRequest;
import cn.zzz.openplatform.txlbs.model.search.TxlbsSuggestionResponse;
import cn.zzz.openplatform.txlbs.model.weather.TxlbsWeatherFutureRequest;
import cn.zzz.openplatform.txlbs.model.weather.TxlbsWeatherFutureResponse;
import cn.zzz.openplatform.txlbs.model.weather.TxlbsWeatherNowRequest;
import cn.zzz.openplatform.txlbs.model.weather.TxlbsWeatherNowResponse;

import java.util.*;

/**
 * 腾讯位置服务
 */
public class TxlbsOpenApiService {

    private String key;

    private String sk;

    public TxlbsOpenApiService() {
    }

    public TxlbsOpenApiService(String key, String sk) {
        this.key = key;
        this.sk = sk;
    }

    /**
     * 搜索服务-关键词输入提示
     *
     * @param keyword String 关键词
     * @return TxlbsSuggestionResponse
     */
    public TxlbsSuggestionResponse searchSuggestion(String keyword) {
        TxlbsSuggestionRequest request = new TxlbsSuggestionRequest();
        request.setKeyword(keyword);
        return execute(request);
    }

    /**
     * 根据行政区划代码adcode查询实时天气
     *
     * @param adcode String
     * @return TxlbsWeatherNowResponse
     */
    public TxlbsWeatherNowResponse weatherNowByAdcode(String adcode) {
        TxlbsWeatherNowRequest request = new TxlbsWeatherNowRequest();
        request.setAdcode(adcode);
        return execute(request);
    }

    /**
     * 根据行政区划代码adcode查询预报天气
     *
     * @param adcode String
     * @return TxlbsWeatherNowResponse
     */
    public TxlbsWeatherFutureResponse weatherFutureByAdcode(String adcode) {
        return weatherFutureByAdcode(adcode, null);
    }

    /**
     * 根据行政区划代码adcode查询预报天气
     *
     * @param adcode String
     * @return TxlbsWeatherNowResponse
     */
    public TxlbsWeatherFutureResponse weatherFutureByAdcode(String adcode, Integer getMd) {
        TxlbsWeatherFutureRequest request = new TxlbsWeatherFutureRequest();
        request.setAdcode(adcode);
        request.setGet_md(getMd);
        return execute(request);
    }

    /**
     * 根据经纬度查询实时天气
     *
     * @param location TxlbsLocation
     * @return TxlbsWeatherNowResponse
     */
    public TxlbsWeatherNowResponse weatherNowByLocation(TxlbsLocation location) {
        TxlbsWeatherNowRequest request = new TxlbsWeatherNowRequest();
        request.setLocation(location.getLat() + "," + location.getLng());
        return execute(request);
    }

    /**
     * 根据经纬度查询预报天气
     *
     * @param location TxlbsLocation
     * @return TxlbsWeatherNowResponse
     */
    public TxlbsWeatherFutureResponse weatherFutureByLocation(TxlbsLocation location) {
        return weatherFutureByLocation(location, null);
    }

    /**
     * 根据经纬度查询预报天气
     *
     * @param location TxlbsLocation
     * @return TxlbsWeatherNowResponse
     */
    public TxlbsWeatherFutureResponse weatherFutureByLocation(TxlbsLocation location, Integer getMd) {
        TxlbsWeatherFutureRequest request = new TxlbsWeatherFutureRequest();
        request.setLocation(location.getLat() + "," + location.getLng());
        request.setGet_md(getMd);
        return execute(request);
    }

    private <T extends TxlbsOpenApiResponse> T execute(TxlbsOpenApiRequest<T> request) {
        Map<String, Object> paramMap = buildParams(request);

        HttpRequest httpRequest = HttpRequest.get(TxlbsUrlConstant.BASE_URL + request.getUrl()).form(paramMap);
        T result;
        try (HttpResponse execute = httpRequest.execute()) {
            result = JSONUtil.toBean(execute.body(), request.getResponseClass());
        }
        return result;
    }

    private <T extends TxlbsOpenApiResponse> Map<String, Object> buildParams(TxlbsOpenApiRequest<T> request) {
        Map<String, Object> paramMap = new TreeMap<>(request.buildParams());
        paramMap.put("key", key);
        if (sk != null && !sk.isEmpty()) {
            paramMap.put("sig", sign(request.getUrl(), paramMap));
        }
        return paramMap;
    }

    /**
     * 签名计算
     * 1. 首先对参数进行排序：按参数名升序
     * 2. 请求路径+"?"+请求参数+SK进行拼接，并计算拼接后字符串md5值（字符必须为小写），即为签名(sig)
     *
     * @param params 请求参数
     * @return sig
     */
    private String sign(String url, Map<String, Object> params) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(url).append("?");
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        for (String key1 : keys) {
            Object value = params.get(key1);
            if (value != null) {
                stringBuilder.append(key1).append("=").append(value).append("&");
            }
        }
        if (stringBuilder.length() > 0 && stringBuilder.charAt(stringBuilder.length() - 1) == '&') {
            stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        }
        stringBuilder.append(sk);
        return SecureUtil.md5(stringBuilder.toString()).toLowerCase();
    }
}
